From 69d9746708ef36f1bfdc855acc073fc7aa6e1045 Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 10 Oct 2014 20:38:46 +0200 Subject: [PATCH] wayland: write wl_data_offer data asynchronously Currently writing wl_data_offer data into the fd is 1) synchronous, which is noticeable when transferring large amounts of data, and 2) buggy, write() error checking is done on the accumulator, breaking both the written data accounting and error checking itself. Fix both by making writes asynchonous through GOutputStream, the operation is spun off and either finished, or cancelled if new data is stored in the selection while the transfer is active. --- gdk/wayland/gdkselection-wayland.c | 114 ++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 17 deletions(-) diff --git a/gdk/wayland/gdkselection-wayland.c b/gdk/wayland/gdkselection-wayland.c index 98c693068b..52d85352b0 100644 --- a/gdk/wayland/gdkselection-wayland.c +++ b/gdk/wayland/gdkselection-wayland.c @@ -20,6 +20,7 @@ #include #include +#include #include #include "gdkwayland.h" @@ -33,6 +34,7 @@ typedef struct _SelectionBuffer SelectionBuffer; typedef struct _StoredSelection StoredSelection; +typedef struct _AsyncWriteData AsyncWriteData; struct _SelectionBuffer { @@ -48,6 +50,7 @@ struct _SelectionBuffer struct _StoredSelection { GdkWindow *source; + GCancellable *cancellable; guchar *data; gsize data_len; GdkAtom type; @@ -60,6 +63,12 @@ struct _DataSourceData GdkAtom selection; }; +struct _AsyncWriteData { + GOutputStream *stream; + GdkWaylandSelection *selection; + gsize index; +}; + enum { ATOM_CLIPBOARD, ATOM_DND @@ -87,6 +96,7 @@ struct _GdkWaylandSelection }; static void selection_buffer_read (SelectionBuffer *buffer); +static void async_write_data_write (AsyncWriteData *write_data); static void selection_buffer_notify (SelectionBuffer *buffer) @@ -272,6 +282,12 @@ gdk_wayland_selection_free (GdkWaylandSelection *selection) g_free (selection->stored_selection.data); + if (selection->stored_selection.cancellable) + { + g_cancellable_cancel (selection->stored_selection.cancellable); + g_object_unref (selection->stored_selection.cancellable); + } + if (selection->stored_selection.fd > 0) close (selection->stored_selection.fd); @@ -366,34 +382,91 @@ gdk_wayland_selection_emit_request (GdkWindow *window, gdk_event_free (event); } -static gboolean -gdk_wayland_selection_check_write (GdkWaylandSelection *selection) +static AsyncWriteData * +async_write_data_new (GdkWaylandSelection *selection) { - gssize len, bytes_written = 0; - gchar *buf; + AsyncWriteData *write_data; - if (selection->stored_selection.fd < 0 || - selection->stored_selection.data_len == 0) - return FALSE; + write_data = g_slice_new0 (AsyncWriteData); + write_data->selection = selection; + write_data->stream = + g_unix_output_stream_new (selection->stored_selection.fd, TRUE); + + return write_data; +} - len = selection->stored_selection.data_len; - buf = (gchar *) selection->stored_selection.data; +static void +async_write_data_free (AsyncWriteData *write_data) +{ + g_object_unref (write_data->stream); + g_slice_free (AsyncWriteData, write_data); +} - while (len > 0) +static void +async_write_data_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + AsyncWriteData *write_data = user_data; + GError *error = NULL; + gsize bytes_written; + + bytes_written = g_output_stream_write_finish (G_OUTPUT_STREAM (object), + res, &error); + if (error) { - bytes_written += write (selection->stored_selection.fd, - buf + bytes_written, len); + g_warning ("Error writing selection data: %s", error->message); + g_error_free (error); - if (bytes_written < 0) - break; + async_write_data_free (write_data); + return; + } + + write_data->index += bytes_written; - len -= bytes_written; + if (write_data->index < + write_data->selection->stored_selection.data_len) + { + /* Write the next chunk */ + async_write_data_write (write_data); } + else + async_write_data_free (write_data); +} - close (selection->stored_selection.fd); +static void +async_write_data_write (AsyncWriteData *write_data) +{ + GdkWaylandSelection *selection = write_data->selection; + gsize buf_len; + guchar *buf; + + buf = selection->stored_selection.data; + buf_len = selection->stored_selection.data_len; + + g_output_stream_write_async (write_data->stream, + &buf[write_data->index], + buf_len - write_data->index, + G_PRIORITY_DEFAULT, + selection->stored_selection.cancellable, + async_write_data_cb, + write_data); +} + +static gboolean +gdk_wayland_selection_check_write (GdkWaylandSelection *selection) +{ + AsyncWriteData *write_data; + + if (selection->stored_selection.fd < 0 || + selection->stored_selection.data_len == 0) + return FALSE; + + write_data = async_write_data_new (selection); + async_write_data_write (write_data); selection->stored_selection.fd = -1; - return bytes_written != 0; + return TRUE; } void @@ -435,10 +508,17 @@ gdk_wayland_selection_store (GdkWindow *window, g_free (selection->stored_selection.data); } + if (selection->stored_selection.cancellable) + { + g_cancellable_cancel (selection->stored_selection.cancellable); + g_object_unref (selection->stored_selection.cancellable); + } + selection->stored_selection.source = window; selection->stored_selection.data_len = array->len; selection->stored_selection.data = (guchar *) g_array_free (array, FALSE); selection->stored_selection.type = type; + selection->stored_selection.cancellable = g_cancellable_new (); gdk_wayland_selection_check_write (selection); } -- 2.30.2